package br.edu.ufcg.greengrid.model.job;

import br.edu.ufcg.greengrid.model.machine.Machine;

public class Job implements Comparable<Job> {

	private static long CURRENT_ID = 0;
	
	private String id;
	
	protected final long runtime;

	protected long runtimeCounter;

	private final long submitTime;

	private long makespan;
	
	protected long timeToOperationsFactor;
	
	public static final long DEFAULT_TIME_TO_OPERATIONS_FACTOR = 1L;
	
	private long failures;
	
	public Job(long runtime) {
		this(0L, runtime);
	}

	public Job(long submitTime, long runtime) {
		this(submitTime, runtime, DEFAULT_TIME_TO_OPERATIONS_FACTOR);
	}

	public Job(long submitTime, long runtime, long timeToOperationsFactor) {
		this(String.valueOf(CURRENT_ID++), submitTime, runtime, timeToOperationsFactor);
	}
	
	public Job(String id, long submitTime, long runtime, long timeToOperationsFactor) {
		
		this.runtime = runtime;
		this.submitTime = submitTime;
		this.id = id;
		this.timeToOperationsFactor = timeToOperationsFactor;
		this.failures = 0L;
		this.runtimeCounter = this.runtime;
		normalizeRuntime(Machine.DEFAULT_OPERATIONS_PER_TIME);
	}

	public void update() {
		if (this.runtimeCounter > 0) {
			this.runtimeCounter--;
		} else {
			throw new IllegalStateException(String.format("The Job '%s' was updated but it was finished already! Expected runtime: %d. Submite time: %d. Makespan: %d.", this.getId(), this.runtime, this.submitTime, this.makespan));
		}
	}

	public boolean done() {
		return this.runtimeCounter == 0;
	}

	public void clear() {
		normalizeRuntime(Machine.DEFAULT_OPERATIONS_PER_TIME);
	}
	
	public void normalizeRuntime(long operationsPerTime) {
		normalizeRuntime(operationsPerTime, this.runtime);
	}
	
	protected void normalizeRuntime(long operationsPerTime, long runtime) {
		this.runtimeCounter = (long)Math.ceil(1.0 * runtime * this.timeToOperationsFactor / operationsPerTime);
	}

	public String getId() {
		return id;
	}

	public long getRuntime() {
		return runtime;
	}

	public long getSubmitTime() {
		return submitTime;
	}

	public long getMakespan() {
		return makespan;
	}
	
	public void incMakespan() {
		this.makespan++;
	}

	public long getFailures() {
		return failures;
	}
	
	public void incFailures() {
		this.failures++;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (int) (makespan ^ (makespan >>> 32));
		result = prime * result + (int) (runtime ^ (runtime >>> 32));
		result = prime * result + (int) (runtimeCounter ^ (runtimeCounter >>> 32));
		result = prime * result + (int) (submitTime ^ (submitTime >>> 32));
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Job other = (Job) obj;
		if (makespan != other.makespan)
			return false;
		if (runtime != other.runtime)
			return false;
		if (runtimeCounter != other.runtimeCounter)
			return false;
		if (submitTime != other.submitTime)
			return false;
		return true;
	}

	@Override
	public int compareTo(Job o) {

		if(this.id != o.id){
			return this.id.compareTo(o.id);
		}
		
		return 0;
	}

}
